package com.threeti.mecool.mobile.controller;

import com.threeti.mecool.core.application.MemoService;
import com.threeti.mecool.core.application.message.MessageService;
import com.threeti.mecool.core.application.project.ProjectService;
import com.threeti.mecool.core.domain.model.Communication;
import com.threeti.mecool.core.domain.model.Memo;
import com.threeti.mecool.core.domain.model.UserLocation;
import com.threeti.mecool.core.domain.model.acl.User;
import com.threeti.mecool.core.domain.model.basicdata.biz.ChannelSync;
import com.threeti.mecool.core.domain.model.project.Project;
import com.threeti.mecool.core.infrastructure.persistence.CommunicationRepository;
import com.threeti.mecool.core.infrastructure.persistence.MemoRepository;
import com.threeti.mecool.core.infrastructure.persistence.UserLocationRepository;
import com.threeti.mecool.core.infrastructure.persistence.acl.UserRepository;
import com.threeti.mecool.mobile.Commander;
import com.threeti.mecool.mobile.interfaces.BodyType;
import com.threeti.mecool.mobile.interfaces.ResponserDto;
import com.threeti.mecool.mobile.interfaces.assembler.ProjectAssembler;
import com.threeti.mecool.mobile.interfaces.assembler.UserAssembler;
import com.threeti.mecool.mobile.interfaces.dto.CommunicationDto;
import com.threeti.mecool.mobile.interfaces.dto.MemoDto;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.validation.Valid;
import java.util.Date;
import java.util.List;
import java.util.Set;

import static com.threeti.mecool.mobile.Commander.command;
import static com.threeti.mecool.mobile.MessageConst.*;
import static com.threeti.mecool.mobile.interfaces.PageResponseDto.toPage;

/**
 * @author Jay Meng
 */
@Controller
@RequestMapping(value = "/user")
//TODO jay:考虑如何通用处理用户不存在或无效,异常以通用方式前置抛出
//TODO jay:validation CDI how?
public class UserMobiController {
    private static final Logger logger = LoggerFactory.getLogger(UserMobiController.class);

    @Autowired
    private UserRepository userRepo;

    @Autowired
    private CommunicationRepository commRepo;

    @Autowired
    private ProjectService projService;

    @Autowired
    private MemoService memoService;

    @Autowired
    private MemoRepository memoRepo;

    @Autowired
    private UserLocationRepository userLocationRepository;

    @Autowired
    private MessageService messageService;

    @RequestMapping(value = "/changepassword")
    @ResponseBody
    public ResponserDto changePassword(@RequestParam(required = true) String loginName, @RequestParam(required = true) String oldPassword, @RequestParam(required = true) String newPassword) {
        String command = command();

        if (StringUtils.isBlank(newPassword)) {
            return ResponserDto.withBizFailureMsg(command, MSG_USER_001);
        }

        int cnt = userRepo.changePasswordByLoginNameAndOldPassword(loginName, oldPassword, newPassword);
        if (cnt == 0) {
            return ResponserDto.withBizFailureMsg(command, MSG_USER_002);
        }

        return ResponserDto.withBizSuccessMsg(command, MSG_COMMON_002);
    }

    @RequestMapping(value = "/communications")
    @ResponseBody
    public ResponserDto communications(@RequestParam String userLoginName, Pageable pageable) {
        String command = command();
        pageable = new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), Sort.Direction.DESC, "recordDate");
        Page<Communication> comms = commRepo.findByUser_LoginName(userLoginName, pageable);
        if (!comms.hasContent()) {
            return ResponserDto.withBizFailureMsg(command, MSG_USER_003);
        }

        return
                ResponserDto.successCommand(command())
                        .withBizSuccessPageBody("communications", UserAssembler.toDtoList(comms.getContent()), toPage(comms.getTotalElements(), comms.hasNextPage(), comms.getNumber()));
    }

    @RequestMapping(value = "/savecommunication")
    @ResponseBody
    public ResponserDto saveCommunication(@Valid CommunicationDto commDto, BindingResult result) {
        String command = command();
        if (result.hasFieldErrors("projId") || result.hasFieldErrors("projStoreId"))
            return ResponserDto.withBizFailureMsg(command, MSG_USER_004);

        if (result.hasFieldErrors("title") || result.hasFieldErrors("contactName") || result.hasFieldErrors("content"))
            return ResponserDto.withBizFailureMsg(command, MSG_USER_005);

        try {
            commRepo.saveAndFlush(UserAssembler.toDom(commDto));
        } catch (Exception e) {
            logger.error(command, e);
            return ResponserDto.withBizFailureMsg(command, MSG_USER_006);
        }

        return ResponserDto.withBizSuccessMsg(command, MSG_COMMON_001);
    }

    @RequestMapping(value = "/projectwithstores")
    @ResponseBody
    public ResponserDto ownProjectWithStores(@RequestParam(required = true) String userLoginName) {
        String command = command();
        User targetUser = userRepo.findByloginName(userLoginName);
        List<Project> projects = projService.findByUserOwn(targetUser);
        if (CollectionUtils.isEmpty(projects)) {
            return ResponserDto.withBizFailureMsg(command, MSG_COMMON_003);
        }
        return ResponserDto.successCommand(command)
                .withBizSuccessBody("projwithstores", ProjectAssembler.toDtoList(projects, targetUser, projService), BodyType.LIST);
    }

    @RequestMapping("/projects")
    @ResponseBody
    public ResponserDto ownedProjects(@RequestParam String userLoginName) {
        String command = command();
        User targetUser = userRepo.findByloginName(userLoginName);
        List<Project> projects = projService.findByUserOwn(targetUser);
        return ResponserDto.successCommand(command)
                .withBizSuccessBody("projwithstores", ProjectAssembler.toDto(projects), BodyType.LIST);
    }

    @RequestMapping(value = "/memos")
    @ResponseBody
    public ResponserDto memos(@RequestParam(required = true) String userLoginName, Pageable pageable) {
        String command = command();
        pageable = new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), Sort.Direction.DESC, "recordDate");
        Page<Memo> memos = memoRepo.findByUser_LoginName(userLoginName, pageable);
        if (!memos.hasContent()) {
            return ResponserDto.withBizFailureMsg(command, MSG_USER_003);
        }

        return
                ResponserDto.successCommand(command)
                        .withBizSuccessPageBody("memos", UserAssembler.toDtoList1(memos.getContent()), toPage(memos.getTotalElements(), memos.hasNextPage(), memos.getNumber()));
    }

    @RequestMapping(value = "/channelsassociatedwithprojects")
    @ResponseBody
    public ResponserDto channelsAssociatedWithProjectsOf(@RequestParam(required = true) String userLoginName) {
        String command = command();
        Set<ChannelSync> channels = memoService.findChannelsSharedByProjectsOf(userLoginName);
        if (CollectionUtils.isEmpty(channels)) {
            return ResponserDto.withBizFailureMsg(command, MSG_COMMON_003);
        }

        return
                ResponserDto.successCommand(command)
                        .withBizSuccessBody("channels", UserAssembler.toDtoList(channels), BodyType.LIST);
    }

    @RequestMapping(value = "/savememo")
    @ResponseBody
    public ResponserDto saveMemo(@Valid MemoDto memoDto, BindingResult result) {
        String command = command();
        if (result.hasFieldErrors("channelCode"))
            return ResponserDto.withBizFailureMsg(command, MSG_USER_008);

        if (result.hasFieldErrors("content"))
            return ResponserDto.withBizFailureMsg(command, MSG_USER_009);

        Memo memo;
        try {
            memo = memoRepo.saveAndFlush(UserAssembler.toDom(memoDto));
        } catch (Exception e) {
            logger.error(command, e);
            return ResponserDto.withBizFailureMsg(command, MSG_USER_010);
        }

        return ResponserDto.withBizSuccessMsg(command, MSG_COMMON_001).withMsgAttachement("memo", UserAssembler.toDto(memo), BodyType.ENTITY);
    }

    @RequestMapping(value = "/editmemo")
    @ResponseBody
    public ResponserDto editMemo(@Valid MemoDto memoDto, BindingResult result) {
        return saveMemo(memoDto, result);
    }

    @RequestMapping(value = "/removememo")
    @ResponseBody
    public ResponserDto removeMemo(@RequestParam(required = true) String userLoginName, @RequestParam(required = true) String memoId) {
        String command = command();
        int cnt = memoRepo.deleteWithRtnCntBy(Long.valueOf(memoId));
        if (cnt == 0) {
            return ResponserDto.withBizFailureMsg(command, MSG_COMMON_003);
        }

        return ResponserDto.withBizSuccessMsg(command, MSG_COMMON_004);
    }

    @RequestMapping("/uploaduserlocation")
    @ResponseBody
    public ResponserDto uploadUserLocation(@RequestParam("userLoginName") String userLoginName,
                                           @RequestParam("latitude") double latitude,
                                           @RequestParam("longitude") double longitude,
                                           @RequestParam(value = "createdAt", required = false)
                                           @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date createdAt) {
        String command = Commander.command();
        try {
            UserLocation userLocation = new UserLocation();
            userLocation.setUserLoginName(userLoginName);
            userLocation.setLatitude(latitude);
            userLocation.setLongitude(longitude);
            userLocation.setCreatedAt(createdAt != null ? createdAt : new Date());
            userLocationRepository.save(userLocation);
            return ResponserDto.withBizSuccessMsg(command, "OK", "1");
        } catch (Exception e) {
            return ResponserDto.withBizFailureMsg(command, "ERROR", e.toString());
        }
    }

    @RequestMapping("/receivedmessage")
    @ResponseBody
    public ResponserDto saveMessageReceipt(@RequestParam("id") Long messageId) {
        String command = Commander.command();
        try {
            messageService.saveReceipt(messageId, new Date());
            return ResponserDto.withBizSuccessMsg(command, "OK", "1");
        } catch (Exception e) {
            return ResponserDto.withBizFailureMsg(command, "ERROR", e.toString());
        }
    }
}
